package com.yingwei.mining.util;

import java.awt.Point;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.yingwei.mining.control.Data;
import com.yingwei.mining.model.businessobject.Configuration;
import com.yingwei.mining.model.businessobject.Node;
import com.yingwei.mining.model.businessobject.Edge;

public class LocationUtil
{
    private static Logger logger = LoggerFactory.getLogger(LocationUtil.class);

    // 60*1852(nautical mile)*1000mm/1000,000 = 111.12 - means a single digit
    // gps
    // value in mining database is equal to 111.12mm, or 0.11112Meter. In other
    // words,
    // the precision of the GPS we used is to the minimum of 0.11112 meter.
    private static final double factor = 111.12;

    public static Node findNearbyNode(double x1, double y1, List<Node> nodes)
    {
	double min = Double.MAX_VALUE;
	Node nearest = null;
	for (Node node : nodes)
	{
	    double dist = LocationUtil.distanceToPoint(x1, y1, node.getX(),
		    node.getY());
	    if (dist < min)
	    {
		min = dist;
		nearest = node;
	    }
	}
	return nearest;
    }

    public static Node findNearbyNode(int x1, int y1, Set<Edge> edges)
    {
	double min = Double.MAX_VALUE;
	Node nearest = null;
	for (Edge edge : edges)
	{
	    Node node = edge.getFromNode();
	    double dist = LocationUtil.distanceToPoint(x1, y1, node.getX(),
		    node.getY());
	    if (dist < min)
	    {
		min = dist;
		nearest = node;
	    }
	    node = edge.getToNode();
	    dist = LocationUtil.distanceToPoint(x1, y1, node.getX(), node
		    .getY());
	    if (dist < min)
	    {
		min = dist;
		nearest = node;
	    }
	}
	if (edges == null || edges.size() == 0)
	{
	    logger.warn("edge is null or size is zero" + edges);
	}
	return nearest;
    }

    public static int getScreenLocationX(int longitude)
    {
	Configuration conf = Data.getData().getConfiguration();
	int x = longitude - conf.getNorthWestLongitude();
	int posX = (int) (x * factor / conf.getMmPerPixel());
	return posX;
    }

    public static int getScreenLocationY(int latitude)
    {
	Configuration conf = Data.getData().getConfiguration();
	int y = conf.getNorthWestLatitude() - latitude;
	int posY = (int) (y * factor / conf.getMmPerPixel());
	return posY;
    }

    public static int getGpsLocationX(int posX)
    {
	Configuration conf = Data.getData().getConfiguration();
	int x = (int) (posX * conf.getMmPerPixel() / factor + conf
		.getNorthWestLongitude());
	return x;
    }

    public static int getGpsLocationY(int posY)
    {
	Configuration conf = Data.getData().getConfiguration();
	int y = (int) (conf.getNorthWestLatitude() - posY
		* conf.getMmPerPixel() / factor);
	return y;
    }

    public static int getGpsToMiliMeterDistance(int gpsDistance)
    {
	// 60*1852(nautical mile)*1000mm/1000,000 = 111.12
	return (int) (gpsDistance * factor);
    }

    public static double distanceToPoint(double x1, double y1, double x2,
	    double y2)
    {
	double dx = x1 - x2;
	double dy = y1 - y2;
	return Math.sqrt(dx * dx + dy * dy);
    }

    public static double distanceToSegment(double x, double y, double x1,
	    double y1, double x2, double y2)
    {
	final Point2D p = new Point2D.Double(x, y);
	final Point2D p1 = new Point2D.Double(x1, y1);
	final Point2D p2 = new Point2D.Double(x2, y2);
	return distanceToSegment(p1, p2, p);
    }

    public static double distanceToSegment(Point2D p1, Point2D p2, Point2D p3)
    {
	final double xDelta = p2.getX() - p1.getX();
	final double yDelta = p2.getY() - p1.getY();

	if ((xDelta == 0) && (yDelta == 0))
	{
	    throw new IllegalArgumentException(
		    "p1 and p2 cannot be the same point");
	}

	final double u = ((p3.getX() - p1.getX()) * xDelta + (p3.getY() - p1
		.getY())
		* yDelta)
		/ (xDelta * xDelta + yDelta * yDelta);

	final Point2D closestPoint;
	if (u < 0)
	{
	    closestPoint = p1;
	}
	else if (u > 1)
	{
	    closestPoint = p2;
	}
	else
	{
	    closestPoint = new Point2D.Double(p1.getX() + u * xDelta, p1.getY()
		    + u * yDelta);
	}

	return closestPoint.distance(p3);
    }
    public static boolean intersectsLine(float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4)
    {
	Line2D.Float line1 = new Line2D.Float(x1,y1,x2,y2); 
	Line2D.Float line2 = new Line2D.Float(x3,y3,x4,y4);
	return line1.intersectsLine(line2);
    }
    /*
     * public static void main(String[] a) { DataHelper data =
     * DataHelper.getDataHelper(); data.init(); for(Node node:data.getNodes()) {
     * node.setX(getGpsLocationX(node.getX()));
     * node.setY(getGpsLocationY(node.getY())); }
     * DataHelper.getDataHelper().close(); }
     */
}
